package com.open.capacity.tenant.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.open.capacity.common.web.CodeEnum;
import com.open.capacity.common.web.Result;
import com.open.capacity.tenant.config.DataSourceConfiguration;
import com.open.capacity.tenant.dao.PropertiesMapper;
import com.open.capacity.tenant.dao.TenantMapper;
import com.open.capacity.tenant.dao.entity.Properties;
import com.open.capacity.tenant.dao.entity.Tenant;
import com.open.capacity.tenant.model.CreateTenantDto;
import com.open.capacity.tenant.model.TenantRouteProperties;
import com.open.capacity.tenant.model.UpdateTenantDto;
import com.open.capacity.tenant.model.convert.TenantConvert;
import com.open.capacity.tenant.service.TenantService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

/**
 * 租户注册业务层
 * @program: open-capacity-platform
 * @author: GuoGaoJu
 * @create: 2023-03-20
 **/
@Slf4j
@Service
public class TenantServiceImpl implements TenantService {

    @Autowired
    private TenantConvert tenantConvert;

    @Autowired
    private TenantMapper tenantMapper;

    @Autowired
    private DataSourceConfiguration dataSourceConfiguration;

    @Autowired
    private PropertiesMapper propertiesMapper;

    @Autowired
    private TenantRouteProperties tenantRouteProperties;

    @Value("${spring.cloud.config.uri}")
    private String configUri;

    /**
     * 租户注册
     * @param createTenantDto
     * @return 注册结果
     */
    @Override
    public String saveTenant(CreateTenantDto createTenantDto) {
        //0.判断租户标识是否为空，@Valid已校验
        //1.根据租户标识tenant_id判断租户是否重复，重复抛出异常
        LambdaQueryWrapper<Tenant> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Tenant::getTenant_id,createTenantDto.getTenantId());
        Tenant tenant = tenantMapper.selectOne(queryWrapper);
        if (tenant != null){
            //tenant_id已存在，返回注册失败
            return "租户标识已存在，注册失败！";
        }
        //2.不重复，注册租户
        Tenant tenant1 = tenantConvert.dto2Po(createTenantDto);
        Date date = new Date();
        tenant1.setCreate_time(date);
        tenant1.setUpdate_time(date);
        int insert = tenantMapper.insert(tenant1);

        return insert==1 ? "注册成功！" : "注册失败！";
    }

    /**
     * 租户状态更新（1-申请中、2-活跃、3-注销）
     * @param updateTenantDto 包含租户标识、状态
     * @return 更新结果
     */
    @Override
    public Result updateTenantStatus(UpdateTenantDto updateTenantDto) {
        //0.租户标识和租户状态非空校验，已使用@Valid注解校验
        //1.根据租户标识查询租户当前状态
        LambdaQueryWrapper<Tenant> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Tenant::getTenant_id,updateTenantDto.getTenant_id());
        Tenant tenant = tenantMapper.selectOne(queryWrapper);
        if (tenant==null){
            return Result.failed(CodeEnum.UPDATE_STATUS_ERROR);
        }

        //2.如果更新状态与原状态一致，更新失败
        if (updateTenantDto.getStatus() == tenant.getStatus()){
            return Result.failed(CodeEnum.STATUS_SAME_ERROR);
        }

        //3.如果是2->1、3->1，更新失败
        if (((tenant.getStatus()==2)&&(updateTenantDto.getStatus()==1))
                ||((tenant.getStatus()==3)&&(updateTenantDto.getStatus()==1)) ){
            return Result.failed(CodeEnum.CANNOT_UPDATE_STATUS_ERROR);
        }

        //4.如果是1->2，为租户创建数据库、写入路由表
        if ((tenant.getStatus()==1)&&(updateTenantDto.getStatus()==2) ){
            //4.1创建数据库
            //数据库名称，tenant_拼接租户表中id
            String schemaName = "tenant_"+tenant.getId();
            //创建数据库--执行flyway版本迁移
            dataSourceConfiguration.migrate(schemaName);

            //4.2写入路由表properties
            Properties properties = new Properties();
            properties.setKey(tenantRouteProperties.getKey()+tenant.getTenant_id()); //spring.schema.tenant-map.{tenant_id}
            properties.setValue(schemaName);
            properties.setApplication(tenantRouteProperties.getApplication());
            properties.setLabel(tenantRouteProperties.getLabel());
            properties.setProfile(tenantRouteProperties.getProfile());
            propertiesMapper.insertProperty(properties);

            //bus刷新 路由信息
            try{
                log.info("开始执行消息总线刷新");
                busRefresh();
                log.info("消息总线刷新完成！");
            }catch (Exception e){
                log.error(e.getMessage(),e);
            }
        }
        //5.如果是2->3，将租户状态修改为注销，只做逻辑删除，不做物理删除。后面租户标识将不能再被使用
        //注销时关闭数据源，修改路由表application为delete  &  路由更新
        if ((tenant.getStatus()==2)&&(updateTenantDto.getStatus()==3) ){
            //5.1.修改路由表,application修改为delete，标识该路由信息对应租户已被注销
            propertiesMapper.deletePropertyByKey(tenantRouteProperties.getKey()+tenant.getTenant_id(),"delete");

            //bus刷新 路由信息
            try{
                log.info("开始执行消息总线刷新");
                busRefresh();
                log.info("消息总线刷新完成！");
            }catch (Exception e){
                log.error(e.getMessage(),e);
            }
        }

        //6.更新租户状态
        tenant.setStatus(updateTenantDto.getStatus());
        tenant.setUpdate_time(new Date());
        tenantMapper.updateById(tenant);

        return Result.succeed("租户状态更新成功！");
    }

    /**
     * bus请求刷新消息总线
     */
    private void busRefresh() throws IOException {
        //新建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String url = configUri+"/actuator/bus-refresh";

        //post请求
        HttpPost post = new HttpPost(url);

        //执行bus刷新
        httpClient.execute(post);
    }

    /**
     * 查询租户列表
     * @return
     */
    @Override
    public Result<List<Tenant>> queryTenantList() {
        List<Tenant> tenantList = tenantMapper.selectList(null);
        return Result.succeed(tenantList,"查询租户列表成功！");
    }
}
